﻿using System;

using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using System.IO.Ports;
using InTheHand.Net.Sockets;
using System.Net.Sockets;
using InTheHand.Net.Bluetooth;
using System.Reflection;
using System.Collections.Specialized;
//using System.Diagnostics;

namespace EVDashboard
{
    public partial class MainForm : Form
    {
        #region Private declarations

        // Bluetooth
        BluetoothClient cli = new BluetoothClient();
        private Stream inStream = null;
        private Thread btReadThread = null;

        private string sdCard = null;
        private int origFuelHeight;
        private int origFuelTop;

        private List<List<float>> data;

        private Brush borderBrush;
        private SolidBrush greenBrush;
        private SolidBrush yellowBrush;
        private SolidBrush redBrush;
        private Pen borderPen;
        private SolidBrush blackBrush;
        private SolidBrush whiteBrush;
        private Font barFont;

        private ISystem bms;

        private float dangerLimit = 0f;
        private float warningLimit = 0f;

        private AlertForm aform;

        private bool AutoConnect = false;
        private String bmsName = null; // set by App.config or Properties.Resources
        #endregion

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, System.EventArgs e)
        {
            // DrawString instead of text
            origFuelHeight = fuelPicBox.Height;
            origFuelTop = fuelPicBox.Top;// 61;
            fuelText.Hide();
            tempText.Hide();
            kWhText.Hide();
            sdCard = Util.GetStorageCard();
            
            // battery variables
            borderBrush = new SolidBrush(Color.Black);
            greenBrush = new SolidBrush(Color.Green);
            yellowBrush = new SolidBrush(Color.Yellow);
            redBrush = new SolidBrush(Color.Red);
            borderPen = new Pen(Color.Black);
            blackBrush = new SolidBrush(Color.Black);
            whiteBrush = new SolidBrush(Color.White);
            barFont = new Font("Verdana", 8.0f, FontStyle.Regular);

            // Display the form
            this.Show();
            this.Update();

            SetControlsFromAppSettings();
            if (bmsName.Equals("PAKTRAKR"))
                bms = new Paktrakr();
            else if (bmsName.Equals("HARDYBMS"))
                bms = new HardyBMS();
            else
                MessageBox.Show("Invalid BMS Name: " + bmsName, "Invalid BMS Name");

            bms.Initialize(this);

            if (!bms.HasConfig())
            {
                tabPage4.Hide();
            }
            else
            {
                SetBmsConfigControls(bms.GetConfig());
            }

            StartBluetooth();

            // Create the alert form
            aform = new AlertForm(this);
        }

        private void StartBluetooth()
        {
            // Try to start the BT radio
            //if (!BluetoothRadio.IsSupported)
            //{
            //    try
            //    {
            //        Process p = new Process();
            //        p.StartInfo.FileName = @"\ResidentFlash2\YFAP30\BluetoothNav.exe";
            //        p.Start();
            //        Thread.Sleep(2000);
            //        p.Kill();
            //    }
            //    catch (Win32Exception we)
            //    {
            //        if (we.NativeErrorCode == 2) // File Not Found
            //            MessageBox.Show("File Not Found", "Bluetooth Startup Error");
            //        else if (we.NativeErrorCode == 4) // No File Permissions
            //            MessageBox.Show("Invalid File Permissions", "Bluetooth Startup Error");
            //        else
            //            MessageBox.Show(we.Message + " " + we.NativeErrorCode, "Bluetooth Startup Error");
            //    }
            //    catch (Exception e)
            //    {
            //        MessageBox.Show(e.Message, "Bluetooth Startup Error");
            //    }
            //}

            // Auto connect if the bluetooth radio is on
            if (BluetoothRadio.IsSupported && AutoConnect)
            {
                DiscoverBluetooth();
                if (bluetoothCombo.Items.Count > 0)
                {
                    BluetoothDeviceInfo device = (BluetoothDeviceInfo)bluetoothCombo.Items[0];
                    ConnectBluetooth(device);
                }
            }

        }

        private void SetControlsFromAppSettings()
        {
            // Init from Properties
            bmsName = Properties.Resources.BMS;
            batteryTypeCombo.SelectedItem = Properties.Resources.BatteryType;
            simulationCheckBox.Checked = false;
            logCheckBox.Checked = false;
            AutoConnect = false;

            // if the App.Settings file exists use it to set controls 
            NameValueCollection AppSettings = Util.LoadConfig();
            if (AppSettings != null && AppSettings.Count > 0)
            {
                String localBmsName = AppSettings.Get(Util.BMS);
                if (localBmsName != null)
                {
                    bmsName = localBmsName.ToUpper();
                }

                String batteryType = AppSettings.Get(Util.BATTERY_TYPE);
                if (batteryType != null)
                {
                    batteryTypeCombo.SelectedItem = batteryType;
                }

                String simulation = AppSettings.Get(Util.SIMULATION).ToUpper();
                if (simulation != null)
                {
                    if (simulation.Equals("TRUE"))
                        simulationCheckBox.Checked = true;
                }

                String logging = AppSettings.Get(Util.LOGGING).ToUpper();
                if (logging != null)
                {
                    if (logging.Equals("TRUE"))
                        logCheckBox.Checked = true;
                }

                String autoConnect = AppSettings.Get(Util.AUTOCONNECT).ToUpper();
                if (autoConnect != null)
                {
                    if (autoConnect.Equals("TRUE"))
                        AutoConnect = true;
                }
            }
        }

        public void SetData(List<List<float>> newdata)
        {
            if (data != null && data.Count != newdata.Count)
            {
                remotePicBox1.Show();
                remotePicBox2.Show();
                remotePicBox3.Show();
                remotePicBox4.Show();
                remotePicBox5.Show();
                remotePicBox6.Show();
            }

            //  If the data is different redraw the remote image
            if (tabControl1.SelectedIndex == 1) // tabPage2
            {
                for (int i=0; i<data.Count; i++)
                {
                    Boolean redraw = false;
                    for (int j = 0; j < data[i].Count; j++)
                    {
                        if (data[i][j] != newdata[i][j])
                            redraw = true;
                    }

                    if (redraw)
                    {
                        remotePicBoxRefresh(i);
                    }
                }
            }

            data = newdata;
        }

        private void remotePicBoxRefresh(int remote)
        {
            if (remote == 0)
                remotePicBox1.Refresh();
            else if (remote == 1)
                remotePicBox2.Refresh();
            else if (remote == 2)
                remotePicBox3.Refresh();
            else if (remote == 3)
                remotePicBox4.Refresh();
            else if (remote == 4)
                remotePicBox5.Refresh();
            else if (remote == 5)
                remotePicBox6.Refresh();
        }

        private void remotePicBox_Paint(object sender, PaintEventArgs e, int remoteNumber)
        {
            int barW = 45;
            int firstBar = 55;
            int barSpacing = 5; // between bars
            int borderMargin = 5;

            int x = borderMargin;
            int y = borderMargin;

            Graphics g = e.Graphics;
            SizeF sf = g.MeasureString("12.5V", barFont);
            int count = 0;

            if (data == null)
                return;

            PictureBox picBox = (PictureBox)sender;
            if (remoteNumber >  data.Count)
            {
                picBox.Hide();
                return;
            }

            List<float>remote = data[remoteNumber-1];

            x = borderMargin + barSpacing;
            g.DrawString("Row " + remoteNumber, barFont, whiteBrush, x, y + barSpacing);
            x += firstBar;

            foreach (float voltage in remote)
            {
                // Horizontal graph
                count++;
                string s = " " + Math.Round(Convert.ToDecimal(voltage), 2); // +"V";
                if (voltage < dangerLimit) // red
                {
                    //g.FillRectangle(redBrush, x, y, barW, barH);
                    g.DrawImage(batteryImages.Images[2], x, y);
                    g.DrawString(s, barFont, blackBrush, x, y+5);
                }
                else if (voltage < warningLimit) // yellow
                {
                    //g.FillRectangle(yellowBrush, x, y, barW, barH);
                    g.DrawImage(batteryImages.Images[1], x, y);
                    g.DrawString(s, barFont, blackBrush, x, y+5);
                }
                else // green
                {
                    //g.FillRectangle(greenBrush, x, y, barW, barH);
                    g.DrawImage(batteryImages.Images[0], x, y);
                    g.DrawString(s, barFont, blackBrush, x, y+5);
                }
                x += barW + barSpacing;
            }

            base.OnPaint(e);
        }

        // Used to transfer data from read thread to graphics thread
        public delegate void SetTextDeleg(string text);
        public delegate void SetDataDeleg(BmsData bmsData);
        public delegate void SetAlertDeleg(BmsAlert bmsAlert);
        public delegate void SetConfigDeleg(BmsConfig bmsConfig);

        // Simulation (data string)
        private void m_DataReceived(string line)
        {
            m_DataReceived(bms.Parse(line));
        }

        public void m_DataReceived(BmsData bmsData)
        {
            if (!logCheckBox.Checked)
            {
                try {
                    Util.LogData(bmsData.line, sdCard);
                }  
                catch (Exception e)
                {
                    statusBar1.Text = e.Message;
                    statusBar1.Update();
                }
            }

            string socText = bmsData.soc + "%";
            if (!socText.Equals(fuelText.Text))
            {
                // Scale the fuel image
                int fuelHeight = (int)(origFuelHeight * bmsData.soc / 100f);
                this.fuelPicBox.Size = new Size(fuelPicBox.Width, fuelHeight);
                this.fuelPicBox.Top = origFuelTop + origFuelHeight - fuelHeight;
                this.fuelText.Text = socText; // this is hidden but is used for DrawString.
                this.fuelPicBox.Update();
            }

            string ampsText;
            if (bmsData.amps == 0.0f)
                ampsText = "0.0";
            else
                ampsText = "" + (float)Math.Round(bmsData.amps, 2);
            if (!this.ampsText.Text.Equals(ampsText))
            {
                this.ampsText.Text = ampsText;
                this.ampsText.Update();
            }

            string voltsText = "" + (float)Math.Round(bmsData.voltage, 1);
            if (!this.voltsText.Text.Equals(voltsText))
            {
                this.voltsText.Text = voltsText;
                this.voltsText.Update();
            }
            string tempText;
            
            tempText = "" + bmsData.temp + "°C";
            if (!metricButton.Checked)
            {
                tempText = "" + ((bmsData.temp * 9 / 5) + 32) + "°F";
            }
            
            if (!this.tempText.Text.Equals(tempText))
            {
                this.tempText.Text = tempText;
                this.tempText.Update();
            }

            string wattHourText = "" + Math.Round(bmsData.kwh, 2); // +" kWh";
            if (!this.kWhText.Text.Equals(wattHourText))
            {
                this.kWhText.Text = wattHourText;
                this.kWhText.Update();
            }

            // Set the battery voltages
            SetData(bmsData.voltages);

            // Set the running time on the status bar
            statusBar1.Text = "Uptime: " + bmsData.uptime.ToString();
            statusBar1.Update();
        }

        public void m_ConfigReceived(BmsConfig bmsConfig)
        {
            SetBmsConfigControls(bmsConfig);
        }

        public void m_AlertReceived(BmsAlert bmsAlert)
        {
            if (bmsAlert.Severity == BmsAlert.SEVERITY_ERROR)
            {
                Util.Play("\\Windows\\infintr.wav");
                if (aform == null)
                {
                    aform = new AlertForm(this);
                }
                aform.AddAlert(bmsAlert);
                aform.Show(); 
            }
            else
            {
                statusBar1.Text = bmsAlert.Notice;
                statusBar1.Update();
            }
        }

        private void simulation() {
            StreamReader sr = null;
            try
            {
               //SDMMC or Storage Card
                sr = new StreamReader(sdCard + "\\" + bms.GetSimFileName());
            }
            catch (DirectoryNotFoundException dnf)
            {
                MessageBox.Show(dnf.Message);
                return;
            }
            catch (FileNotFoundException fnf)
            {
                MessageBox.Show(fnf.Message);
                return;
            }

            string line = null;
            while ((line = sr.ReadLine()) != null)
            {
                this.Invoke(new SetTextDeleg(m_DataReceived), new object[] { line }); 
                Thread.Sleep(1000);
            }
            sr.Close();
        }

        private void fuelPicBox_Paint(object sender, PaintEventArgs e)
        {
            DrawLabel(fuelText, (PictureBox)sender, e.Graphics);
        }

        private void connectButton_Click(object sender, EventArgs e)
        {
            bms.SetBmsSetup(CreateBmsSetupFromControls());

            if (simulationCheckBox.Checked)
            {
                Thread simulationThread = new Thread(new ThreadStart(simulation));
                simulationThread.Start();
                return;
            }

            BluetoothDeviceInfo device = (BluetoothDeviceInfo)bluetoothCombo.SelectedItem;
            statusBar1.Text = "Connecting to " + device.DeviceName + "...";
            statusBar1.Update();

            Boolean connected = ConnectBluetooth(device);

            if (connected)
            {
                statusBar1.Text = "Connected";
                statusBar1.Update();
            }
            else
            {
                statusBar1.Text = "Connection error";
                statusBar1.Update();
            }
        }

        private void ThreadFunction()
        {
            bms.Manager(inStream);
        }

        private Boolean ConnectBluetooth(BluetoothDeviceInfo device)
        {
            // Get the Pin from the setup tab
            cli.SetPin(device.DeviceAddress, btPairText.Text);
            cli.Connect(device.DeviceAddress, BluetoothService.SerialPort);
            inStream = cli.GetStream();

            if (inStream.CanRead)
            {
                btReadThread = new Thread(new ThreadStart(ThreadFunction));
                btReadThread.Start();
            }
            else
            {
                MessageBox.Show("Cannot read Bluetooth Stream", "Error");
                inStream.Close();
            }

            return inStream.CanRead;
        }

        private void btDiscover_Click(object sender, EventArgs e)
        {
            statusBar1.Text = "Discovering Bluetooth devices...";
            statusBar1.Update();

            if (BluetoothRadio.IsSupported)
                DiscoverBluetooth();
            else
                MessageBox.Show("BlueTooth Radio is Off");

            statusBar1.Text = "Ready";
            statusBar1.Update();
        }

        private void DiscoverBluetooth()
        {
            BluetoothDeviceInfo[] peers = cli.DiscoverDevices();
            bluetoothCombo.DataSource = peers;
            bluetoothCombo.DisplayMember = "DeviceName";
            bluetoothCombo.Update();
        }

        private void MainForm_Closing(object sender, CancelEventArgs e)
        {
            if (cli.Connected)
            {
                if (btReadThread != null)
                {
                    btReadThread.Abort();
                }
                inStream.Close();
                cli.Close();
            }
        }

        private void mainPicBox_Paint(object sender, PaintEventArgs e)
        {
            DrawLabel(tempText, (PictureBox)sender, e.Graphics);
            DrawLabel(kWhText, (PictureBox)sender, e.Graphics);
        }

        private void DrawLabel(Label label, PictureBox parent, Graphics grx)
        {
            int labelTop = label.Top - parent.Top;
            int labelLeft = label.Left - parent.Left;
            
            if (label.TextAlign == ContentAlignment.TopLeft)
            {
                Rectangle labelBounds = new Rectangle(labelLeft, labelTop, label.Width, label.Height);
                grx.DrawString(label.Text, label.Font, new SolidBrush(label.ForeColor), labelBounds);
            }
            else if (label.TextAlign == ContentAlignment.TopCenter)
            {
                SizeF size = grx.MeasureString(label.Text, label.Font);
                int left = this.Width / 2 - (int)size.Width / 2;
                Rectangle rect = new Rectangle(left, labelTop, (int)size.Width, (int)label.Height);
                grx.DrawString(label.Text, label.Font, new SolidBrush(label.ForeColor), rect);
            }
            else if (label.TextAlign == ContentAlignment.TopRight)
            {
                SizeF size = grx.MeasureString(label.Text, label.Font);
                int left = label.Width - (int)size.Width + label.Left;
                Rectangle rect = new Rectangle(left, labelTop, (int)size.Width, (int)size.Height);
                grx.DrawString(label.Text, label.Font, new SolidBrush(label.ForeColor), rect);
            }
        }

        private void remotePicBox1_Paint(object sender, PaintEventArgs e)
        {
            remotePicBox_Paint(sender, e, 1);
        }

        private void remotePicBox2_Paint(object sender, PaintEventArgs e)
        {
            remotePicBox_Paint(sender, e, 2);
        }

        private void remotePicBox3_Paint(object sender, PaintEventArgs e)
        {
            remotePicBox_Paint(sender, e, 3);
        }

        private void remotePicBox4_Paint(object sender, PaintEventArgs e)
        {
            remotePicBox_Paint(sender, e, 4);
        }

        private void remotePicBox5_Paint(object sender, PaintEventArgs e)
        {
            remotePicBox_Paint(sender, e, 5);
        }

        private void remotePicBox6_Paint(object sender, PaintEventArgs e)
        {
            remotePicBox_Paint(sender, e, 6);
        }

        private void SetBmsConfigControls(BmsConfig bmsConfig)
        {
            // Set local variables
            dangerLimit = bmsConfig.UnderVoltage;
            warningLimit = bmsConfig.UnderVoltage + (bmsConfig.OverVoltage - bmsConfig.UnderVoltage) * 0.1f;

            overVoltageTextBox.Text = bmsConfig.OverVoltage.ToString();
            overVoltageTextBox.Update();

            balanceVoltageTextBox.Text = bmsConfig.BalanceVoltage.ToString();
            balanceVoltageTextBox.Update();

            underVoltageTextBox.Text = bmsConfig.UnderVoltage.ToString();
            underVoltageTextBox.Update();

            highTempTextBox.Text = bmsConfig.HighTemp.ToString();
            highTempTextBox.Update();

            maxKwhTextBox.Text = bmsConfig.MaxKwH.ToString();
            maxKwhTextBox.Update();

            balanceCyclesNumeric.Value = bmsConfig.BalanceCycles;
            balanceCyclesNumeric.Update();

            //numberOfBoardsTextBox.Text = bmsConfig.Boards.Count.ToString();
            numberOfBoardsTextBox.Text = bmsConfig.NumberOfBoards.ToString();
            numberOfBoardsTextBox.Update();

            if (bmsConfig.Boards != null)
            {
                cellsOnBoard1TextBox.Text = bmsConfig.Boards[0].ToString();
                cellsOnBoard1TextBox.Update();

                cellsOnBoard2TextBox.Text = bmsConfig.Boards[1].ToString();
                cellsOnBoard2TextBox.Update();

                cellsOnBoard3TextBox.Text = bmsConfig.Boards[2].ToString();
                cellsOnBoard3TextBox.Update();

                cellsOnBoard4TextBox.Text = bmsConfig.Boards[3].ToString();
                cellsOnBoard4TextBox.Update();

                cellsOnBoard5TextBox.Text = bmsConfig.Boards[4].ToString();
                cellsOnBoard5TextBox.Update();
            }
            //CellsBoard6_config.Text = bmsConfig.CellsPerBoard[5].ToString();
            //CellsBoard6_config.Update();
            //CellsBoard7_config.Text = bmsConfig.CellsPerBoard[6].ToString();
            //CellsBoard7_config.Update();

            maxKwhTextBox.Text = bmsConfig.MaxKwH.ToString();
            maxKwhTextBox.Update();

            statusBar2.Text = bmsConfig.Status;
            statusBar2.Update();
        }
        
        public void WriteStatus(string thisstatus)
        {
            statusBar2.Text = thisstatus;
            statusBar2.Update();
        }

        private BmsSetup CreateBmsSetupFromControls()
        {
            BmsSetup bmsSetup = new BmsSetup();

            // From the Setup tab
            bmsSetup.BatteryType = batteryTypeCombo.Text;
            bmsSetup.LogEnabled = logCheckBox.Checked;
            bmsSetup.SimEnabled = simulationCheckBox.Checked;

            return bmsSetup;
        }

        private void WriteBmsConfig()
        {
            // Build the bms config
            BmsConfig bmsConfig = new BmsConfig();

            // From the Config tab
            bmsConfig.OverVoltage = float.Parse(overVoltageTextBox.Text);

            bmsConfig.BalanceVoltage = float.Parse(balanceVoltageTextBox.Text);

            bmsConfig.UnderVoltage = float.Parse(underVoltageTextBox.Text);

            bmsConfig.HighTemp = float.Parse(highTempTextBox.Text);

            bmsConfig.MaxKwH = float.Parse(maxKwhTextBox.Text);

            balanceCyclesNumeric.Value = bmsConfig.BalanceCycles;
            
            bmsConfig.NumberOfBoards = int.Parse(numberOfBoardsTextBox.Text);

            bmsConfig.Boards = new List<int>();
            bmsConfig.Boards.Add(int.Parse(cellsOnBoard1TextBox.Text));
            bmsConfig.Boards.Add(int.Parse(cellsOnBoard2TextBox.Text));
            bmsConfig.Boards.Add(int.Parse(cellsOnBoard3TextBox.Text));
            bmsConfig.Boards.Add(int.Parse(cellsOnBoard4TextBox.Text));
            bmsConfig.Boards.Add(int.Parse(cellsOnBoard5TextBox.Text));


            //CellsBoard6_config.Text = bmsConfig.CellsPerBoard[5].ToString();
            //CellsBoard6_config.Update();
            //CellsBoard7_config.Text = bmsConfig.CellsPerBoard[6].ToString();
            //CellsBoard7_config.Update();

            bmsConfig.Mode = modeComboBox.Text;
            bmsConfig.BalanceCycles = (int)balanceCyclesNumeric.Value;

            // write config to bms
            bms.WriteConfig(bmsConfig);
        }

        private void writeConfigToBms_Click(object sender, EventArgs e)
        {
            WriteBmsConfig();
        }

        private void TextBox_GotFocus(object sender, EventArgs e)
        {
            inputPanel.Enabled = true;
        }

        private void TextBox_LostFocus(object sender, EventArgs e)
        {
            inputPanel.Enabled = false;
        } 
 

    }
}